如何在不修改物件的情況上,使用中間層(Adapter)後,能轉換跟其他物件聯繫,同時原有功能不受影響。
這模式在現實最典型的應用是充電器:使用 110v 的電子設備,在國外 220v 的環境下,需要使用「變壓器」讓「充電」這個目的,仍能實踐。
應用在軟體開發上,可能有這些應用:
XML
,新系統只提供 JSON
,需要中間層負責轉換。簡而言之,就是建立一個「轉換器」,讓系統維持運作。
在實作前,要注意的是判斷環境中,目標物件本身是單一物件還是繼承虛擬層的物件?而作法則是:
以下 UML 圖與範例程式碼,採用「目標物件本身繼承虛擬層」。
親代、虛擬層:Car
public abstract class Car {
protected String name;
protected Car(String name) {
this.name = name;
}
public void turnOn() {
System.out.println("發動車子,名稱是:" + name);
}
public void turnOff() {
System.out.println("車子熄火");
}
public abstract void turnRight();
public abstract void turnLeft();
public void speedUp() {
System.out.println("腳踩油門");
}
public void brake() {
System.out.println("腳踩煞車");
}
}
子代:LeftHandCar
、RightHandCar
(Adaptee)
public class LeftHandCar extends Car {
public LeftHandCar(String name) {
super(name);
}
@Override
public void turnRight() {
System.out.println("打右轉燈");
System.out.println("車輛靠右");
System.out.println("確認沒有行人");
System.out.println("確認沒有機車");
System.out.println("車輛右轉");
}
@Override
public void turnLeft() {
System.out.println("打左轉燈");
System.out.println("車輛向左靠近分隔島");
System.out.println("車輛向前靠近左轉區塊");
System.out.println("等待左轉燈亮起");
System.out.println("左轉燈亮起");
System.out.println("車輛左轉");
}
}
public class RightHandCar extends Car {
public RightHandCar(String name) {
super(name);
}
@Override
public void turnRight() {
System.out.println("打右轉燈");
System.out.println("車輛向右靠近分隔島");
System.out.println("車輛向前靠近右轉區塊");
System.out.println("等待右轉燈亮起");
System.out.println("右轉燈亮起");
System.out.println("車輛右轉");
}
@Override
public void turnLeft() {
System.out.println("打左轉燈");
System.out.println("車輛靠左");
System.out.println("確認沒有行人");
System.out.println("確認沒有機車");
System.out.println("車輛左轉");
}
}
右駕車適應在左駕環境:RightHandInLeftHandTraffic
(Adapter)
public class RightHandInLeftHandTraffic extends RightHandCar {
public RightHandInLeftHandTraffic(String name) {
super(name);
}
@Override
public void turnRight() {
System.out.println("打右轉燈");
System.out.println("車輛靠右");
System.out.println("確認沒有行人");
System.out.println("確認沒有機車");
System.out.println("車輛右轉");
}
@Override
public void turnLeft() {
System.out.println("打左轉燈");
System.out.println("車輛向左靠近分隔島");
System.out.println("車輛向前靠近左轉區塊");
System.out.println("等待左轉燈亮起");
System.out.println("左轉燈亮起");
System.out.println("車輛左轉");
}
}
在左駕的環境上路:LeftHandTraffic
public class LeftHandTraffic {
public static void main(String[] args) {
System.out.println("---國產車上路囉---");
Car domesticCar = new LeftHandCar("Altis");
domesticCar.turnOn();
domesticCar.speedUp();
domesticCar.brake();
domesticCar.turnRight();
domesticCar.speedUp();
domesticCar.brake();
domesticCar.turnLeft();
domesticCar.turnOff();
System.out.println("\n---完美,下車換日本進口車---");
Car madeInJapanCar = new RightHandInLeftHandTraffic("CT 200h");
madeInJapanCar.turnOn();
madeInJapanCar.speedUp();
madeInJapanCar.brake();
madeInJapanCar.turnRight();
madeInJapanCar.speedUp();
madeInJapanCar.brake();
madeInJapanCar.turnLeft();
madeInJapanCar.turnOff();
System.out.println("\n---測試完成,左駕右駕都很棒---");
}
}
親代、虛擬層:Car
/** @abstract */
class Car {
constructor(name) {
this.name = name;
}
turnOn() {
console.log("發動車子,名稱是:" + this.name);
}
turnOff() {
console.log("車子熄火");
}
/** @abstract */
turnRight() { return; }
/** @abstract */
turnLeft() { return; }
speedUp() {
console.log("腳踩油門");
}
brake() {
console.log("腳踩煞車");
}
}
子代:LeftHandCar
、RightHandCar
(Adaptee)
class LeftHandCar extends Car {
constructor(name) {
super(name);
}
/** @override */
turnRight() {
console.log("打右轉燈");
console.log("車輛靠右");
console.log("確認沒有行人");
console.log("確認沒有機車");
console.log("車輛右轉");
}
/** @override */
turnLeft() {
console.log("打左轉燈");
console.log("車輛向左靠近分隔島");
console.log("車輛向前靠近左轉區塊");
console.log("等待左轉燈亮起");
console.log("左轉燈亮起");
console.log("車輛左轉");
}
}
class RightHandCar extends Car {
constructor(name) {
super(name);
}
/** @override */
turnRight() {
console.log("打右轉燈");
console.log("車輛向右靠近分隔島");
console.log("車輛向前靠近右轉區塊");
console.log("等待右轉燈亮起");
console.log("右轉燈亮起");
console.log("車輛右轉");
}
/** @override */
turnLeft() {
console.log("打左轉燈");
console.log("車輛靠左");
console.log("確認沒有行人");
console.log("確認沒有機車");
console.log("車輛左轉");
}
}
右駕車適應在左駕環境:RightHandInLeftHandTraffic
(Adapter)
class RightHandInLeftHandTraffic extends RightHandCar {
constructor(name) {
super(name);
}
/** @override */
turnRight() {
console.log("打右轉燈");
console.log("車輛靠右");
console.log("確認沒有行人");
console.log("確認沒有機車");
console.log("車輛右轉");
}
/** @override */
turnLeft() {
console.log("打左轉燈");
console.log("車輛向左靠近分隔島");
console.log("車輛向前靠近左轉區塊");
console.log("等待左轉燈亮起");
console.log("左轉燈亮起");
console.log("車輛左轉");
}
}
在左駕的環境上路:LeftHandTraffic
const leftHandTraffic = () => {
console.log("---國產車上路囉---");
const domesticCar = new LeftHandCar("Altis");
domesticCar.turnOn();
domesticCar.speedUp();
domesticCar.brake();
domesticCar.turnRight();
domesticCar.speedUp();
domesticCar.brake();
domesticCar.turnLeft();
domesticCar.turnOff();
console.log("\n---完美,下車換日本進口車---");
const madeInJapanCar = new RightHandInLeftHandTraffic("CT 200h");
madeInJapanCar.turnOn();
madeInJapanCar.speedUp();
madeInJapanCar.brake();
madeInJapanCar.turnRight();
madeInJapanCar.speedUp();
madeInJapanCar.brake();
madeInJapanCar.turnLeft();
madeInJapanCar.turnOff();
console.log("\n---測試完成,左駕右駕都很棒---");
}
leftHandTraffic();
Adapter 是非常容易理解的模式,理解是「專接器」後幾乎懂含義。
倒是有幾點要思考:
除了今天介紹的應用,C++
允許多重繼承,所以 Adapter 有另一種寫法,但想想這不是 Java
與 JavaScript
能實作的,便不深究了。
明天將介紹 Structural patterns 的第二個模式:Bridge 模式。